package com.peng.mybatis.scripting.xmltags;

import com.peng.mybatis.reflection.MetaObject;
import com.peng.mybatis.session.Configuration;
import lombok.AllArgsConstructor;
import lombok.Getter;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;

import java.util.HashMap;
import java.util.Map;

public class DynamicContext {

    public static final String PARAMTER_OBJECT_KEY = "_parameter";

    public static final String DATABASE_ID_KEY = "_databaseId";

    static {
        OgnlRuntime.setPropertyAccessor(ContextMap.cl);
    }

    @Getter
    private final ContextMap bindings;

    private final StringBuilder sqlBuilder = new StringBuilder();

    private int uniqueNumber = 0;

    public DynamicContext(Configuration configuration, Object paramObj) {
        if (paramObj instanceof Map) {
            MetaObject metaObject = configuration.newMetaObject(paramObj);
            bindings = new ContextMap(metaObject);
        } else {
            bindings = new ContextMap(null);
        }
        bindings.put(PARAMTER_OBJECT_KEY, paramObj);
        bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
    }

    public void bind(String name, Object value) {
        bindings.put(name, value);
    }

    public void appendSql(String text) {
        sqlBuilder.append(text).append(" ");
    }

    public String getSql() {
        return sqlBuilder.toString().trim();
    }

    public int getUniqueNumber() {
        return uniqueNumber++;
    }

    @AllArgsConstructor
    static class ContextMap extends HashMap<String, Object> {

        private MetaObject paramMetaObject;

        @Override
        public Object get(Object key) {
            String keyStr = key.toString();
            if (super.containsKey(keyStr)) {
                return super.get(keyStr);
            }
            // 利用ognl表达式找，例如：person[0].birthdate.year
            if (paramMetaObject != null) {
                return paramMetaObject.getValue(keyStr);
            }
            return null;
        }

    }

    static class ContextAccessor implements PropertyAccessor {

        @Override
        public Object getProperty(Map context, Object tar, Object name) throws OgnlException {
            if (tar instanceof Map<?, ?> targetMap) {
                Object res = targetMap.get(name);
                if (res != null) {
                    return res;
                }
                Object paramObj = targetMap.get(PARAMTER_OBJECT_KEY);
                if (paramObj instanceof Map<?, ?> paramMap) {
                    return paramMap.get(name);
                }
            }

            return null;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
            Map<Object, Object> targetMap = (Map<Object, Object>) target;
            targetMap.put(name, value);

        }

        @Override
        public String getSourceAccessor(OgnlContext ognlContext, Object o, Object o1) {
            return null;
        }

        @Override
        public String getSourceSetter(OgnlContext ognlContext, Object o, Object o1) {
            return null;
        }

    }

}
